#include "common.h"
#include "memory.h"
#include <string.h>
#include <elf.h>

#define ELF_OFFSET_IN_DISK 0

#ifdef HAS_DEVICE
void ide_read(uint8_t *, uint32_t, uint32_t);
#else
void ramdisk_read(uint8_t *, uint32_t, uint32_t);
#endif

#define STACK_SIZE (1 << 20)

void create_video_mapping();
uint32_t get_ucr3();

uint32_t loader() {
	Elf32_Ehdr *elf;
	Elf32_Phdr *ph = NULL;

	uint8_t buf[4096];

#ifdef HAS_DEVICE
	ide_read(buf, ELF_OFFSET_IN_DISK, 4096);
#else
	ramdisk_read(buf, ELF_OFFSET_IN_DISK, 4096);
#endif

	elf = (void*)buf;

	/* TODO: fix the magic number with the correct one */
	const uint32_t elf_magic = 0x464c457f;
	uint32_t *p_magic = (void *)buf;
	nemu_assert(*p_magic == elf_magic);

    /* Load each program segment */
    int i;
    ph = (void*)(buf + elf->e_phoff);
    for(i = 0; i < elf->e_phnum; i++) {
        if(ph[i].p_type != PT_LOAD) continue;

        uint32_t va = ph[i].p_vaddr;
        uint32_t filesz = ph[i].p_filesz;
        uint32_t memsz  = ph[i].p_memsz;
        uint32_t off    = ph[i].p_offset;

#ifdef HAS_DEVICE
        ide_read((uint8_t *)va, ELF_OFFSET_IN_DISK + off, filesz);
#else
        ramdisk_read((uint8_t *)va, ELF_OFFSET_IN_DISK + off, filesz);
#endif
        if (memsz > filesz) {
            memset((void *)(va + filesz), 0, memsz - filesz);
        }

#ifdef IA32_PAGE
        extern uint32_t cur_brk, max_brk;
        uint32_t new_brk = va + memsz - 1;
        if(cur_brk < new_brk) { max_brk = cur_brk = new_brk; }
#endif
    }

	volatile uint32_t entry = elf->e_entry;

#ifdef IA32_PAGE
	mm_malloc(KOFFSET - STACK_SIZE, STACK_SIZE);

#ifdef HAS_DEVICE
	create_video_mapping();
#endif

	write_cr3(get_ucr3());
#endif

	return entry;
}
